package evs.iap;

import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.lang.reflect.Method;
import java.net.URI;
import java.net.URISyntaxException;

import evs.brp.RemotingErrorException;
import evs.identification.NameServerLookupProxy;
import evs.activation.Message;
import evs.activation.Marshaller;



public class Invoker extends evs.brp.Invoker {
	
	private NameServerLookupProxy nsProxy;
	
	public Invoker(String nameServerUri) throws URISyntaxException, RemotingErrorException{
		
		nsProxy = new NameServerLookupProxy();
		nsProxy.bind(new URI(nameServerUri));
				
	}
	
	@Override
	public void delegate(ObjectInput objectInput, ObjectOutput objectOutput) throws RemotingErrorException {
		log.info("streams arrived. delegate 'em...");
		
		try {

			Message message = null;
			Message returnMessage = new Message();
			Object returnObject = null;
			Method method = null;
			Object myInstance = null;
			boolean oneway = false;

			try {
				
				/**
				 * demarshalling object input
				 */
				message = Marshaller.demarshall(objectInput);
								
				/**
				 * remote object method invoke via reflection
				 */
				Class clazz = Class.forName(message.getClazz());
				
				Class argClazzes[] = new Class[message.getArgumentclazzes().size()];
	            for (int i=0; i < message.getArgumentclazzes().size(); i++) {
	            	argClazzes[i] = message.getArgumentclazzes().get(i);
	            }
				
			     method = clazz.getMethod(message.getMethodName(), argClazzes);
				
				
				/**
				 * perform lookup
				 */
				myInstance = nsProxy.getEntry(message.getClientId(),
						message.getObjectInstanceName());
					
				/**
				 * instance isn't existing yet -> first invoke?
				 */
				if(myInstance == null) {
					myInstance = clazz.newInstance();
				}
					
				oneway = (message.getReturnClazz().getCanonicalName().equals("void"));
				if (oneway) log.info("ONEWAY");
				
				if (!oneway) {
				/**
				 * perform invoke
				 */
				returnObject = method.invoke(myInstance, message.getArguments().toArray());
				}	

				/**
				 * put processed instance (back) to nameservice
				 */
				myInstance = nsProxy.putEntry(message.getClientId(),
						message.getObjectInstanceName(),
							myInstance);
				
				/**
				 * error during put
				 */
				if(myInstance == null) {
					throw new Exception("nsProxy::putEntry failed");
				}
				

			} catch (Exception exc) {
				
				RemotingErrorException re = new RemotingErrorException(exc);
				
				/**
				 * remote object call raised exception -> transport in message
				 */
				returnMessage = new Message();
				if (oneway) {
					returnMessage.setReturnClazz(Boolean.class);
					returnMessage.setReturnValue(new Boolean(false));
				}
				returnMessage.setException(re);
				
				Marshaller.marshall(objectOutput, returnMessage);
				objectOutput.flush();
				
				log.info("exception occured - message written back");
				
				throw re;
				
			}
				
			log.info("try to write back");
			
			/*
			 * marshalling return value of method or acknowledgement
			 */
			if (oneway) {
				returnMessage.setReturnClazz(Boolean.class);
				returnMessage.setReturnValue(new Boolean(true));
				Marshaller.marshall(objectOutput, returnMessage);
				
				objectOutput.flush();
					
				log.info("ACK written back");			
				/**
				 * perform invoke
				 */
				method.invoke(myInstance, message.getArguments().toArray());
			} else {
				returnMessage.setReturnClazz(message.getReturnClazz());
				returnMessage.setReturnValue(message.getReturnClazz().cast(returnObject));
				Marshaller.marshall(objectOutput, returnMessage);
				
				objectOutput.flush();
					
				log.info("message written back");			
			}

			if (oneway) {
			}	
			
		} catch (RemotingErrorException re) {
			throw re;
		} catch (Exception exc) {
			throw new RemotingErrorException(exc);
		}		
	}
}
